<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片转ASCII码工具</title>
    <style>
        :root {
            --bg-color: #f8f9fa;
            --text-color: #212529;
            --primary-color: #0d6efd;
            --secondary-color: #6c757d;
            --border-color: #dee2e6;
            --card-bg: #ffffff;
            --hover-color: #e9ecef;
        }

        .dark-mode {
            --bg-color: #212529;
            --text-color: #f8f9fa;
            --primary-color: #0d6efd;
            --secondary-color: #adb5bd;
            --border-color: #495057;
            --card-bg: #343a40;
            --hover-color: #495057;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            line-height: 1.6;
            transition: background-color 0.3s, color 0.3s;
            padding: 20px;
        }

        header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 30px;
            padding-bottom: 15px;
            border-bottom: 1px solid var(--border-color);
        }

        h1 {
            font-size: 1.8rem;
            color: var(--primary-color);
        }

        .theme-toggle {
            background: none;
            border: none;
            font-size: 1.5rem;
            cursor: pointer;
            color: var(--text-color);
            transition: transform 0.2s;
        }

        .theme-toggle:hover {
            transform: scale(1.1);
        }

        .container {
            display: grid;
            grid-template-columns: 1fr 2fr;
            gap: 20px;
            max-width: 1200px;
            margin: 0 auto;
        }

        .card {
            background-color: var(--card-bg);
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            padding: 20px;
            transition: transform 0.3s;
        }

        .upload-section {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .upload-area {
            width: 100%;
            height: 200px;
            border: 2px dashed var(--border-color);
            border-radius: 8px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            transition: border-color 0.3s, background-color 0.3s;
            margin-bottom: 20px;
            position: relative;
        }

        .upload-area:hover {
            border-color: var(--primary-color);
            background-color: var(--hover-color);
        }

        .upload-area i {
            font-size: 3rem;
            margin-bottom: 10px;
            color: var(--secondary-color);
        }

        .upload-area p {
            color: var(--secondary-color);
            text-align: center;
        }

        .image-preview {
            max-width: 300px;
            max-height: 300px;
            margin: 15px 0;
            border-radius: 8px;
            display: none;
        }

        .file-input {
            display: none;
        }

        .result-section {
            display: flex;
            flex-direction: column;
        }

        .ascii-output {
            background-color: var(--card-bg);
            border-radius: 8px;
            padding: 15px;
            overflow: auto;
            height: 400px;
            font-family: Consolas, monospace;
            white-space: pre;
            line-height: 1;
            font-size: 10px;
            margin-bottom: 20px;
            border: 1px solid var(--border-color);
        }

        .inverted {
            background-color: var(--text-color);
            color: var(--bg-color);
        }

        .controls {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 15px;
            margin-bottom: 20px;
        }

        .control-group {
            margin-bottom: 15px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
        }

        .slider-container {
            display: flex;
            align-items: center;
        }

        .slider {
            flex-grow: 1;
            height: 5px;
            background-color: var(--border-color);
            outline: none;
            border-radius: 5px;
            -webkit-appearance: none;
        }

        .slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            width: 15px;
            height: 15px;
            border-radius: 50%;
            background-color: var(--primary-color);
            cursor: pointer;
        }

        .slider-value {
            min-width: 40px;
            text-align: right;
            margin-left: 10px;
        }

        select {
            width: 100%;
            padding: 8px;
            border-radius: 4px;
            border: 1px solid var(--border-color);
            background-color: var(--card-bg);
            color: var(--text-color);
        }

        .button-group {
            display: flex;
            justify-content: space-between;
            margin-top: 20px;
        }

        .btn {
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: transform 0.2s, background-color 0.2s;
        }

        .btn:hover {
            transform: translateY(-2px);
        }

        .btn:active {
            transform: translateY(0);
            opacity: 0.9;
        }

        .btn-primary {
            background-color: var(--primary-color);
            color: white;
        }

        .btn-secondary {
            background-color: var(--secondary-color);
            color: white;
        }

        .btn-outline {
            background-color: transparent;
            border: 1px solid var(--border-color);
            color: var(--text-color);
        }

        .toggle-btn {
            display: flex;
            align-items: center;
            justify-content: space-between;
            width: 100%;
            padding: 8px 12px;
            border-radius: 4px;
            border: 1px solid var(--border-color);
            background-color: var(--card-bg);
            color: var(--text-color);
            cursor: pointer;
        }

        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 40px;
            height: 20px;
        }

        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--secondary-color);
            transition: .4s;
            border-radius: 20px;
        }

        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 2px;
            bottom: 2px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }

        input:checked + .toggle-slider {
            background-color: var(--primary-color);
        }

        input:checked + .toggle-slider:before {
            transform: translateX(20px);
        }

        .loading {
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0, 0, 0, 0.5);
            color: white;
            justify-content: center;
            align-items: center;
            border-radius: 8px;
            font-size: 1.2rem;
        }

        .loading::after {
            content: "处理中...";
            animation: dots 1.5s infinite;
        }

        @keyframes dots {
            0%, 20% { content: "处理中."; }
            40% { content: "处理中.."; }
            60%, 100% { content: "处理中..."; }
        }

        .notification {
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 10px 20px;
            background-color: var(--primary-color);
            color: white;
            border-radius: 4px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            transform: translateY(100px);
            opacity: 0;
            transition: transform 0.3s, opacity 0.3s;
        }

        .notification.show {
            transform: translateY(0);
            opacity: 1;
        }

        footer {
            margin-top: 30px;
            text-align: center;
            color: var(--secondary-color);
            font-size: 0.9rem;
            padding-top: 20px;
            border-top: 1px solid var(--border-color);
        }

        /* 移动端适配 */
        @media (max-width: 768px) {
            .container {
                grid-template-columns: 1fr;
            }

            .controls {
                grid-template-columns: 1fr;
            }

            .button-group {
                flex-direction: column;
                gap: 10px;
            }

            .btn {
                width: 100%;
            }

            header {
                flex-direction: column;
                align-items: flex-start;
            }

            .theme-toggle {
                position: absolute;
                top: 20px;
                right: 20px;
            }
        }
    </style>
</head>
<body>
    <header>
        <h1>图片转ASCII码工具</h1>
        <button class="theme-toggle" id="themeToggle">🌙</button>
    </header>

    <div class="container">
        <div class="card upload-section">
            <div class="upload-area" id="uploadArea">
                <div class="loading" id="loading"></div>
                <i>📁</i>
                <p>点击或拖拽图片到此处上传<br><small>支持jpg/png/webp格式，≤5MB</small></p>
                <input type="file" id="fileInput" class="file-input" accept="image/jpeg,image/png,image/webp">
            </div>
            <img id="imagePreview" class="image-preview" alt="预览图">
        </div>

        <div class="result-section">
            <pre id="asciiOutput" class="ascii-output">ASCII结果将显示在这里...</pre>
            
            <div class="controls">
                <div class="control-group">
                    <label for="widthSlider">字符密度 (宽度)</label>
                    <div class="slider-container">
                        <input type="range" id="widthSlider" class="slider" min="50" max="200" value="100">
                        <span id="widthValue" class="slider-value">100</span>
                    </div>
                </div>
                
                <div class="control-group">
                    <label for="charsetSelect">字符集</label>
                    <select id="charsetSelect">
                        <option value="simple">精简版 (10字符)</option>
                        <option value="full">完整版 (32字符)</option>
                    </select>
                </div>
                
                <div class="control-group">
                    <label>反色效果</label>
                    <button id="invertBtn" class="toggle-btn">
                        <span>黑底白字</span>
                        <label class="toggle-switch">
                            <input type="checkbox" id="invertToggle">
                            <span class="toggle-slider"></span>
                        </label>
                    </button>
                </div>
                
                <div class="control-group">
                    <label for="fontSizeSlider">字体大小</label>
                    <div class="slider-container">
                        <input type="range" id="fontSizeSlider" class="slider" min="6" max="16" value="10">
                        <span id="fontSizeValue" class="slider-value">10px</span>
                    </div>
                </div>
            </div>
            
            <div class="button-group">
                <button id="copyBtn" class="btn btn-primary" disabled>复制ASCII</button>
                <button id="downloadBtn" class="btn btn-secondary" disabled>下载结果</button>
                <button id="resetBtn" class="btn btn-outline">重置</button>
            </div>
        </div>
    </div>

    <footer>
        <p>图片转ASCII码工具 - 纯前端实现，无需联网，保护您的隐私</p>
    </footer>

    <div class="notification" id="notification"></div>

    <script>
        // DOM元素
        const uploadArea = document.getElementById('uploadArea');
        const fileInput = document.getElementById('fileInput');
        const imagePreview = document.getElementById('imagePreview');
        const asciiOutput = document.getElementById('asciiOutput');
        const widthSlider = document.getElementById('widthSlider');
        const widthValue = document.getElementById('widthValue');
        const charsetSelect = document.getElementById('charsetSelect');
        const invertToggle = document.getElementById('invertToggle');
        const fontSizeSlider = document.getElementById('fontSizeSlider');
        const fontSizeValue = document.getElementById('fontSizeValue');
        const copyBtn = document.getElementById('copyBtn');
        const downloadBtn = document.getElementById('downloadBtn');
        const resetBtn = document.getElementById('resetBtn');
        const themeToggle = document.getElementById('themeToggle');
        const notification = document.getElementById('notification');
        const loading = document.getElementById('loading');

        // 状态变量
        let currentImage = null;
        let fileName = '';
        let isDarkMode = false;

        // ASCII字符集
        const charsets = {
            simple: ' .,:;ox%#@',  // 精简版 (10字符)
            full: ' .`^",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'  // 完整版 (32字符)
        };

        // 初始化
        function init() {
            // 事件监听
            uploadArea.addEventListener('click', () => fileInput.click());
            fileInput.addEventListener('change', handleFileSelect);
            uploadArea.addEventListener('dragover', handleDragOver);
            uploadArea.addEventListener('dragleave', handleDragLeave);
            uploadArea.addEventListener('drop', handleDrop);
            
            widthSlider.addEventListener('input', updateWidthValue);
            widthSlider.addEventListener('change', processImage);
            
            charsetSelect.addEventListener('change', processImage);
            
            invertToggle.addEventListener('change', toggleInvert);
            
            fontSizeSlider.addEventListener('input', updateFontSize);
            
            copyBtn.addEventListener('click', copyAscii);
            downloadBtn.addEventListener('click', downloadAscii);
            resetBtn.addEventListener('click', resetApp);
            
            themeToggle.addEventListener('click', toggleTheme);

            // 初始化字体大小
            updateFontSize();
        }

        // 文件处理函数
        function handleFileSelect(e) {
            const file = e.target.files[0];
            processFile(file);
        }

        function handleDragOver(e) {
            e.preventDefault();
            e.stopPropagation();
            uploadArea.style.borderColor = 'var(--primary-color)';
            uploadArea.style.backgroundColor = 'var(--hover-color)';
        }

        function handleDragLeave(e) {
            e.preventDefault();
            e.stopPropagation();
            uploadArea.style.borderColor = 'var(--border-color)';
            uploadArea.style.backgroundColor = '';
        }

        function handleDrop(e) {
            e.preventDefault();
            e.stopPropagation();
            uploadArea.style.borderColor = 'var(--border-color)';
            uploadArea.style.backgroundColor = '';
            
            const file = e.dataTransfer.files[0];
            processFile(file);
        }

        // 处理上传的文件
        function processFile(file) {
            // 验证文件类型
            if (!file || !file.type.match('image/(jpeg|png|webp)')) {
                showNotification('请上传图片格式文件 (jpg/png/webp)');
                return;
            }
            
            // 验证文件大小
            if (file.size > 5 * 1024 * 1024) {
                showNotification('文件需≤5MB');
                return;
            }
            
            fileName = file.name;
            
            // 读取文件
            const reader = new FileReader();
            reader.onload = function(e) {
                const img = new Image();
                img.onload = function() {
                    currentImage = img;
                    displayPreview(img);
                    processImage();
                };
                img.src = e.target.result;
            };
            reader.readAsDataURL(file);
        }

        // 显示图片预览
        function displayPreview(img) {
            // 计算预览图尺寸，保持比例
            const maxSize = 300;
            let width = img.width;
            let height = img.height;
            
            if (width > height) {
                if (width > maxSize) {
                    height = height * (maxSize / width);
                    width = maxSize;
                }
            } else {
                if (height > maxSize) {
                    width = width * (maxSize / height);
                    height = maxSize;
                }
            }
            
            // 显示预览图
            imagePreview.src = img.src;
            imagePreview.style.display = 'block';
            imagePreview.style.width = `${width}px`;
            imagePreview.style.height = `${height}px`;
            
            // 启用按钮
            copyBtn.disabled = false;
            downloadBtn.disabled = false;
        }

        // 处理图片转ASCII
        function processImage() {
            if (!currentImage) return;
            
            // 显示加载动画
            loading.style.display = 'flex';
            
            // 使用setTimeout让UI有时间更新
            setTimeout(() => {
                const width = parseInt(widthSlider.value);
                const charset = charsets[charsetSelect.value];
                
                // 计算高度，保持比例
                const ratio = currentImage.height / currentImage.width;
                const height = Math.floor(width * ratio * 0.5);  // 乘0.5是因为字符在终端中高宽比约为2:1
                
                // 创建canvas
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = width;
                canvas.height = height;
                
                // 绘制图片到canvas
                ctx.drawImage(currentImage, 0, 0, width, height);
                
                // 获取像素数据
                const imageData = ctx.getImageData(0, 0, width, height);
                const pixels = imageData.data;
                
                // 转换为ASCII
                let asciiImage = '';
                
                // 对于大图片，采用隔行采样以提高性能
                const skipFactor = width > 150 ? 2 : 1;
                
                for (let y = 0; y < height; y += skipFactor) {
                    for (let x = 0; x < width; x += skipFactor) {
                        const idx = (y * width + x) * 4;
                        
                        // 计算灰度值 (使用加权平均法)
                        const r = pixels[idx];
                        const g = pixels[idx + 1];
                        const b = pixels[idx + 2];
                        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
                        
                        // 映射灰度值到字符
                        const charIndex = Math.floor(gray / 256 * charset.length);
                        asciiImage += charset[charIndex];
                    }
                    asciiImage += '\n';
                }
                
                // 显示ASCII结果
                asciiOutput.textContent = asciiImage;
                
                // 隐藏加载动画
                loading.style.display = 'none';
            }, 100);
        }

        // 更新宽度值显示
        function updateWidthValue() {
            widthValue.textContent = widthSlider.value;
        }

        // 更新字体大小
        function updateFontSize() {
            const size = fontSizeSlider.value;
            fontSizeValue.textContent = `${size}px`;
            asciiOutput.style.fontSize = `${size}px`;
        }

        // 切换反色效果
        function toggleInvert() {
            if (invertToggle.checked) {
                asciiOutput.classList.add('inverted');
            } else {
                asciiOutput.classList.remove('inverted');
            }
        }

        // 复制ASCII到剪贴板
        function copyAscii() {
            const text = asciiOutput.textContent;
            navigator.clipboard.writeText(text)
                .then(() => showNotification('已复制到剪贴板'))
                .catch(err => showNotification('复制失败: ' + err));
        }

        // 下载ASCII结果
        function downloadAscii() {
            const text = asciiOutput.textContent;
            const blob = new Blob([text], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            
            const a = document.createElement('a');
            a.href = url;
            a.download = `ascii_${fileName.split('.')[0]}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
            
            showNotification('文件已下载');
        }

        // 重置应用
        function resetApp() {
            currentImage = null;
            fileName = '';
            imagePreview.style.display = 'none';
            asciiOutput.textContent = 'ASCII结果将显示在这里...';
            widthSlider.value = 100;
            updateWidthValue();
            charsetSelect.value = 'simple';
            invertToggle.checked = false;
            toggleInvert();
            fontSizeSlider.value = 10;
            updateFontSize();
            copyBtn.disabled = true;
            downloadBtn.disabled = true;
            fileInput.value = '';
        }

        // 切换深色/浅色模式
        function toggleTheme() {
            isDarkMode = !isDarkMode;
            if (isDarkMode) {
                document.body.classList.add('dark-mode');
                themeToggle.textContent = '☀️';
            } else {
                document.body.classList.remove('dark-mode');
                themeToggle.textContent = '🌙';
            }
        }

        // 显示通知
        function showNotification(message) {
            notification.textContent = message;
            notification.classList.add('show');
            
            setTimeout(() => {
                notification.classList.remove('show');
            }, 3000);
        }

        // 初始化应用
        init();
    </script>
</body>
</html>